package video

import (
	"context"
	"gitee.com/bobo-rs/creative-framework/pkg/algorithm/uniquecode"
	"gitee.com/bobo-rs/creative-framework/pkg/sredis"
	"gitee.com/bobo-rs/creative-framework/pkg/utils"
	"gitee.com/bobo-rs/innovideo-services/consts"
	"gitee.com/bobo-rs/innovideo-services/enums"
	"gitee.com/bobo-rs/innovideo-services/framework/dao"
	"gitee.com/bobo-rs/innovideo-services/framework/model"
	"gitee.com/bobo-rs/innovideo-services/framework/service"
	"gitee.com/bobo-rs/innovideo-services/library/exception"
	"gitee.com/bobo-rs/innovideo-services/library/services/safety"
	"gitee.com/bobo-rs/innovideo-services/library/services/video"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"time"
)

// SaveVideo 保存视频信息
func (v *sVideo) SaveVideo(ctx context.Context, item model.VideoSaveItem) error {
	// 视频ID是否存在，存在编辑，不存在添加
	if item.Id == 0 {
		item.VideoNo = uniquecode.UniSerialCode(ctx, consts.VideoNoPrefix)
	}

	// 上映时间
	if len(item.ReleaseDate) > 0 {
		item.YearDate = utils.SplitNonCharWords(item.ReleaseDate)[0]
	}

	// 视频推广海报
	item.ImageUrl = service.Attachment().MustAttachUrlByAttachId(ctx, item.AttachId)

	// 保存数据
	return dao.Video.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		r, err := dao.Video.Ctx(ctx).OmitEmpty().Save(item.VideoBasicItem)
		if err != nil {
			return exception.New(`保存视频信息失败`)
		}
		videoId, err := r.LastInsertId()
		if err != nil {
			return exception.New(`保存视频获取视频ID失败`)
		}

		// 保存视频属性
		item.VideoAttribute.VideoId = uint(videoId)
		if err = v.SaveAttribute(ctx, item.VideoAttribute); err != nil {
			return err
		}

		// 保存视频标签
		if len(item.Values) > 0 {
			err = v.SaveLabels(ctx, uint(videoId), item.Values...)
		}
		return err
	})
}

// GetVideoList 获取视频列表
func (v *sVideo) GetVideoList(ctx context.Context, in model.VideoListInput) (out *model.VideoListOutput, err error) {
	out = &model.VideoListOutput{}
	// 指定业务端
	if in.VideoWhereItem.FormSide == enums.FormSideBus {
		in.VideoWhereItem.IsRelease = int(enums.VideoReleaseYes)
		in.VideoWhereItem.Status = int(enums.VideoStatusSuccess)
	}
	// 获取数据列表
	err = v.VideoModel().ListAndTotal(ctx, model.CommonListAndTotalInput{
		Sort:                 v.VideoSort(in.By),
		Where:                v.VideoWhere(ctx, in.VideoWhereItem),
		CommonPaginationItem: in.CommonPaginationItem,
	}, &out.Rows, &out.Total)
	return
}

// GetVideoDetail 获取视频基础详情
func (v *sVideo) GetVideoDetail(ctx context.Context, in model.VideoDetailInput) (detail *model.VideoDetailItem, err error) {
	detail = &model.VideoDetailItem{}
	var (
		where = g.Map{
			dao.Video.Columns().Id: in.VideoId,
		}
		personalWhere = g.Map{
			dao.VideoPersonal.Columns().VideoId: in.VideoId,
		}
	)
	// 按平台端获取搜索条件
	switch in.FormSide {
	case enums.FormSideBus:
		where[dao.Video.Columns().Status] = enums.VideoStatusSuccess
		where[dao.Video.Columns().IsRelease] = enums.VideoReleaseYes
		// 参演人员
		personalWhere[dao.VideoPersonal.Columns().IsShow] = enums.VideoPersonalShow
	}

	// 获取视频详情
	if err = v.VideoModel().Scan(ctx, where, &detail.VideoBasicItem); err != nil {
		return nil, exception.New(`视频不存在或已下架`)
	}

	// 视频属性
	if err = v.AttributeModel().DetailPri(ctx, in.VideoId, &detail.Attribute); err != nil {
		return nil, exception.New(`视频数据不全`)
	}

	// 视频参演人员
	_ = v.PersonalModel().Scan(ctx, personalWhere, &detail.PersonalList)

	// 获取标签值列表
	detail.Values, _ = v.GetLabelValueList(ctx, in.VideoId)
	return
}

// GetVideoPlayEpisodes 获取视频播放续集列表-并验证用户授权
func (v *sVideo) GetVideoPlayEpisodes(ctx context.Context, videoId uint) (out *model.VideoPlayEpisodesOutput, err error) {
	out = &model.VideoPlayEpisodesOutput{}
	// 获取视频授权详情
	out.VideoDetailAuthItem, err = v.GetVideoAuthDetail(ctx, videoId)
	if err != nil {
		return nil, err
	}

	// 获取视频续集列表
	res, err := v.GetEpisodesList(ctx, model.VideoEpisodesListInput{
		VideoId:  videoId,
		FormSide: enums.FormSideBus,
	})
	if err != nil {
		return nil, err
	}
	out.EpisodesList = res.Rows
	out.UpEpisodesNum = res.Total

	// 获取用户购买信息
	out.BuyDetail, _ = service.VideoBuy().GetVideoBuyAuthDetail(ctx, videoId)

	// 获取用户视频播放记录
	if play, _ := service.VideoPlay().GetPlayAuthDetail(ctx, videoId); play != nil {
		out.PlayDetail = *play
	}

	//视频免费，直接返回
	if enums.VideoFeeType(out.FeeType) == enums.VideoFeeTypeFee {
		return
	}

	//迭代处理
	for key := range out.EpisodesList {
		item := &out.EpisodesList[key]
		// 是否已购买
		if out.BuyDetail != nil {
			if out.BuyDetail.Status == uint(enums.VideoBuyStatusYes) || out.BuyDetail.BuyEpisodesResult[item.Id] {
				item.IsBuy = 1
			}
		}

		// 是否已播放
		if out.PlayDetail.PlaySequelResult != nil && out.PlayDetail.PlaySequelResult[item.Id] {
			item.IsPlay = 1
		}

		// 单剧和连续剧
		switch enums.VideoSeries(out.IsSeries) {
		case enums.VideoSeriesMany:
			// 连续剧：是否可以播放，false允许，true禁止
			if item.EpisodesNum > out.FreePlayNum && item.IsBuy == 0 {
				item.IsDisable = true
				item.ResolutionList = nil // 重置播放地址
			}
		}
	}
	return
}

// GetVideoPlayUrl 获取视频播放地址
func (v *sVideo) GetVideoPlayUrl(ctx context.Context, in model.VideoPlayUrlInput) (out *model.VideoPlayUrlOutput, err error) {
	// 整理并获取授权视频信息
	detail, err := v.PrepareUserVideoPlayUrl(ctx, in, service.BizCtx().GetUid(ctx))
	if err != nil {
		return nil, err
	}

	// 获取播放地址
	resolesMap, err := v.GetResolutionMapByEpisodesId(ctx, in.EpisodesId)
	if err != nil {
		return nil, err
	}

	// 生成秘钥
	secret, shuffle := video.New().GenSecret(in.VideoNo, detail.Id)
	out = &model.VideoPlayUrlOutput{
		Shuffle: shuffle,
	}

	// 迭代加密商品地址
	safeHandler := safety.New().AESHandler(secret)
	out.ResolutionList = resolesMap[in.EpisodesId]
	for i := range out.ResolutionList {
		item := &out.ResolutionList[i]
		item.VideoUrl = safeHandler.MustEncryptString([]byte(item.VideoUrl))
	}
	return
}

// GetVideoAuthDetail 获取用户前台授权视频详情
func (v *sVideo) GetVideoAuthDetail(ctx context.Context, videoId uint) (detail *model.VideoDetailAuthItem, err error) {
	// 根据视频ID获取
	return v.queryVideoAuthDetail(ctx, g.Map{
		dao.Video.Columns().Id: videoId,
	})
}

// GetVideoAuthDetailByNo 获取用户前台授权视频详情-视频编号
func (v *sVideo) GetVideoAuthDetailByNo(ctx context.Context, videoNo string) (detail *model.VideoDetailAuthItem, err error) {
	detail = &model.VideoDetailAuthItem{}
	// 查询数据并缓存一天
	r, err := sredis.NewCache().GetOrSetFunc(ctx, consts.VideoAuthDetailNo+videoNo, func(ctx context.Context) (value interface{}, err error) {
		return v.queryVideoAuthDetail(ctx, g.Map{
			dao.Video.Columns().VideoNo: videoNo,
		})
	}, time.Hour*24)
	if err != nil {
		return nil, err
	}
	// 扫描数据
	if err = r.Scan(&detail); err != nil {
		return nil, exception.Newf(`扫描数据到VideoDetailAuthItem失败 %w`, err)
	}
	return
}

// DeleteVideo 删除视频数据
func (v *sVideo) DeleteVideo(ctx context.Context, videoId uint) error {
	if videoId == 0 {
		return exception.New(`视频ID不能为空`)
	}
	// 删除数据
	return dao.Video.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err := tx.Ctx(ctx).Delete(dao.Video.Table(), g.Map{
			dao.Video.Columns().Id:     videoId,
			dao.Video.Columns().Status: enums.VideoStatusDel,
		})
		if err != nil {
			return exception.New(`删除视频失败`)
		}

		// 删除视频属性
		if _, err = tx.Ctx(ctx).Delete(dao.VideoAttribute.Table(), g.Map{
			dao.VideoAttribute.Columns().VideoId: videoId,
		}); err != nil {
			return exception.New(`删除视频属性失败`)
		}

		// 删除视频续集
		if err = v.deleteEpisodesByVideoId(ctx, videoId); err != nil {
			return exception.New(`删除视频续集失败`)
		}

		// 删除相关人员
		if err = v.deletePersonalByVideoId(ctx, videoId); err != nil {
			return exception.New(`删除视频相关人员失败`)
		}

		// 删除标签
		return v.deleteLabelByVideoId(ctx, videoId)
	})
}

// CheckVideoOperatorValid 检测视频操作业务是否有效，例如（是否发布）：当前已发布，需要更改为未发布
func (v *sVideo) CheckVideoOperatorValid(ctx context.Context, videoId uint, operator enums.VideoOperator) bool {
	where := g.Map{
		dao.Video.Columns().Id: videoId,
	}
	switch operator {
	case enums.VideoOperatorTop:
		where[dao.Video.Columns().IsTop] = enums.VideoTopYes
	case enums.VideoOperatorBeat:
		where[dao.Video.Columns().IsBest] = enums.VideoBeatYes
	case enums.VideoOperatorHot:
		where[dao.Video.Columns().IsHot] = enums.VideoHotYes
	case enums.VideoOperatorNew:
		where[dao.Video.Columns().IsNew] = enums.VideoNewYes
	case enums.VideoOperatorRecommend:
		where[dao.Video.Columns().IsRecommend] = enums.VideoRecommendYes
	case enums.VideoOperatorRelease:
		where[dao.Video.Columns().IsRelease] = enums.VideoReleaseYes
	default:
		return false
	}
	// 检测对应业务是否存在
	b, _ := v.VideoModel().Exists(ctx, where)
	return b
}

// PrepareUserVideoPlayUrl 整理用户视频播放地址
func (v *sVideo) PrepareUserVideoPlayUrl(ctx context.Context, in model.VideoPlayUrlInput, uid uint) (detail *model.VideoDetailAuthItem, err error) {
	// 获取授权视频信息
	detail, err = v.GetVideoAuthDetailByNo(ctx, in.VideoNo)
	if err != nil {
		return nil, err
	}
	// 免费视频
	if detail.FeeType == int(enums.VideoFeeTypeVIP) {
		return
	}

	// 是否连续剧
	if detail.IsSeries == uint(enums.VideoSeriesMany) {
		// 获取免费剧集
		freeEpisodesMap, err := v.GetVideoEpisodesIds(ctx, detail.Id, int(detail.FreePlayNum))
		if err != nil {
			return nil, err
		}
		// 免费剧集直接返回
		if freeEpisodesMap[in.EpisodesId] {
			return detail, nil
		}
	}

	// 验证是否已购买剧集
	if !service.VideoBuy().IsPurchased(ctx, uid, detail.Id) {
		return nil, exception.New(`视频未购买，请先购买`)
	}
	return
}

// queryVideoAuthDetail 查询用户前台授权视频详情
func (v *sVideo) queryVideoAuthDetail(ctx context.Context, where g.Map) (detail *model.VideoDetailAuthItem, err error) {
	detail = &model.VideoDetailAuthItem{}

	// 必须状态正常和发布后的视频
	where[dao.Video.Columns().Status] = enums.VideoStatusSuccess
	where[dao.Video.Columns().IsRelease] = enums.VideoReleaseYes

	// 获取视频授权详情
	err = v.VideoModel().Scan(ctx, where, &detail.VideoAuthAttrItem)
	if err != nil {
		return nil, exception.New(`视频信息不存在或已下架`)
	}

	// 视频属性详情
	err = v.AttributeModel().Scan(ctx, g.Map{
		dao.VideoAttribute.Columns().VideoId: detail.Id,
	}, &detail.AttributeAuthItem)
	return
}
